#include "SudokuData.h"
#include "math.h"

#include <QStringList>
#include <QRegExp>
#include <QFile>

SudokuData::SudokuData() : QObject()
{
    sizeBox = 0;
    size = 0;
    lockedCandidates1Count = 0;
    lockedCandidates2Count = 0;
    groupsCount = 0;
    xWingCount = 0;
}

SudokuData::SudokuData(int _sizeBox) : QObject()
{
    sizeBox = _sizeBox;
    size = sizeBox * sizeBox;
    lockedCandidates1Count = 0;
    lockedCandidates2Count = 0;
    groupsCount = 0;
    xWingCount = 0;

    for(int i=0;i<size*size;++i)
        cells.append(SudokuCell(size));
}

SudokuData::SudokuData(QString sudokuNumbers) : QObject()
{
    lockedCandidates1Count = 0;
    lockedCandidates2Count = 0;
    groupsCount = 0;
    xWingCount = 0;

    construct(sudokuNumbers);
}

SudokuData::SudokuData(QString filePath, QString fileName) : QObject()
{
    QByteArray array = readFromFile(filePath, fileName);
    QString sudokuNumbers(array);

    lockedCandidates1Count = 0;
    lockedCandidates2Count = 0;
    groupsCount = 0;
    xWingCount = 0;

    construct(sudokuNumbers);
}

SudokuData::SudokuData(const SudokuData& other) :QObject()//TODO no copy!!!! qobject
{
    size = other.size;
    sizeBox = other.sizeBox;
    lockedCandidates1Count = other.lockedCandidates1Count;
    lockedCandidates2Count = other.lockedCandidates2Count;
    groupsCount = other.groupsCount;
    xWingCount = other.xWingCount;

    cells = QList<SudokuCell>(other.cells);
}

SudokuData::~SudokuData()
{
}

void SudokuData::construct(QString sudokuNumbers)
{
    //TODO XML format
    sudokuNumbers.replace('.','0');
    sudokuNumbers.replace(QChar('x'),QChar('0'),Qt::CaseInsensitive);
    sudokuNumbers = sudokuNumbers.trimmed();
    sudokuNumbers = sudokuNumbers.simplified();
    sudokuNumbers = sudokuNumbers.remove(" ");
    sudokuNumbers = sudokuNumbers.remove("-");//*.ss format support
    sudokuNumbers = sudokuNumbers.remove("|");//*.ss format support
    qDebug() << "SudokuData::construct: " << sudokuNumbers;
    int indexOfFirstNotNumber = sudokuNumbers.indexOf(QRegExp("[^0123456789]"));

    qDebug() << "SudokuData::construct sudokuNumbers.size():" << sudokuNumbers.size();
    QStringList numberList;

    qDebug() << "indexOfFirstNotNumber" << indexOfFirstNotNumber;
    if(indexOfFirstNotNumber == -1 || indexOfFirstNotNumber > 10)//TODO
    {
        qDebug() << "plain format without delimiter";
        for(int i=0;i<sudokuNumbers.size();++i)
        {
            //qDebug() << "SudokuData::construct " << sudokuNumbers.at(i);
            numberList.append(sudokuNumbers.at(i));
        }
    }
    else
    {
        numberList = sudokuNumbers.split(QRegExp("[^0123456789]"), QString::SkipEmptyParts);
    }

    double boxSize = floor( sqrt(sqrt( static_cast<double>(numberList.size()) )) );
    qDebug() << "boxSize: " << boxSize;

    sizeBox = static_cast<int>(boxSize);

    if(sizeBox < 2)
        qFatal("minimum size is 2, not enought data");

    size = sizeBox * sizeBox;

    if(size * size < numberList.size())
        qWarning("wrong data size, not all numbers will be used");

    for(int i=0;i<numberList.size();++i)
    {
        bool ok;
        int cellValue = numberList[i].toInt(&ok);
        if(!ok)
            qFatal("conversion error");//TODO throw?

        if(cellValue < 0 || cellValue > size)
            qFatal("wrong number");//TODO throw?

        if(cellValue == 0)
            cells.append(SudokuCell(size));
        else
            cells.append(SudokuCell(size, cellValue, Certain));
    }

    excludeAllPossibl();
}

/*SudokuData& SudokuData::operator=(const SudokuData& other)//TODO no copy!!!
{
    if (&other == this)
        return *this;

    size = other.size;
    sizeBox = other.sizeBox;
    lockedCandidates1Count = other.lockedCandidates1Count;
    lockedCandidates2Count = other.lockedCandidates2Count;
    groupsCount = other.groupsCount;
    xWingCount = other.xWingCount;

    cells = QList<SudokuCell>(other.cells);

    return *this;
}*/

void SudokuData::excludeAllPossibl()
{
    for(int i=0;i<size;++i)
        for(int j=0;j<size;++j)
            if(!cells[i*size + j].isEmpty())//.getValue() != 0)
                excludePossibl(i,j,cells[i*size + j].getValue());
}

void SudokuData::excludePossibl(int row, int col, int val)
{
   // qDebug() << row << col << val;
    for(int i=0;i<size;++i)
    {
        cells[row*size + i].setPossibl(val-1, false);//remove from row
        cells[size*i + col].setPossibl(val-1, false);//remove from col
    }

    int begW = row - row % sizeBox;
    int begK = col - col % sizeBox;

    for(int i=begW;i<begW+sizeBox;++i)//romove from Box
    {
        for(int j=begK;j<begK+sizeBox;++j)
        {
            cells[i*size + j].setPossibl(val-1, false);
        }
    }
}

int SudokuData::findNakedSingles()
{
    int count = 0;
    for(int i=0;i<cells.size();++i)
        if(cells[i].isEmpty())//.getValue() == 0)
            if(cells[i].countPossible() == 1)
            {
                int ind = 0;
                while(cells[i].getPossibl(ind) == false)
                    ind++;
                onFoundValue(i, ind+1, NakedSingles);
                count++;
            }
    qDebug() << "SudokuData::findNakedSingles count: " << count;
    return count;
}

int SudokuData::findHiddenSinglesInVect(QHash<int, SudokuCell*> map, int insertType)
{
    //qDebug() << "SudokuData::findHiddenSinglesInVect>";
    int count = 0;

    for(int number = 0; number<size; ++number)
    {
        QHashIterator<int, SudokuCell*> i(map);
        int possibleCells = 0;
        int cellIndex = 0;
        while (i.hasNext() && possibleCells < 2)
        {
            i.next();
            if(i.value()->isEmpty())//if empty cell
            {
                //qDebug() << "number: " << number;
                //qDebug()<< i.key();
                // qDebug() << i.value()->getValue() << i.value()->getPrior();
                if(i.value()->getPossibl(number))
                {
                    ++possibleCells;
                    cellIndex = i.key();
                }
                //cout << i.key() << ": " << i.value() << endl;
            }
            else if(i.value()->getValue() == number+1)//if number already set
            {
                possibleCells = 2;//just to break loop
            }
        }
        if(possibleCells == 1)
        {
            onFoundValue(cellIndex,number+1, insertType);
            ++count;
        }
    }
    //qDebug() << "SudokuData::findHiddenSinglesInVect<";
    return count;
}

int SudokuData::findHiddenSingles()
{
    int count = 0;

    for(int i=0;i<size;++i)//cells in row/col/box from 1 to size
    {
        QHash<int, SudokuCell*> rowMap;
        QHash<int, SudokuCell*> colMap;
        QHash<int, SudokuCell*> boxMap;

        int boxBaseIndex = (i%sizeBox)*sizeBox + (i/sizeBox)*size*sizeBox;

        for(int j=0;j<size;++j)
        {
            rowMap[i*size + j] = &cells[i*size + j];
            colMap[j*size + i] = &cells[j*size + i];
            int cellBoxIndex = boxBaseIndex + j%sizeBox + (j/sizeBox)*size;
            boxMap[cellBoxIndex] = &cells[cellBoxIndex];
        }
        count += findHiddenSinglesInVect(rowMap, HiddenSinglesRow);
        count += findHiddenSinglesInVect(colMap, HiddenSinglesCol);
        count += findHiddenSinglesInVect(boxMap, HiddenSinglesBox);

    }

    qDebug() << "SudokuData::findHiddenSingles count: " << count;
    return count;
}

int SudokuData::findLockedCandidates1AndExclude()
{

    int zmienilo=0;
    int juzJest;
    int nrK,nrW;

    for(int liczba=0;liczba<size;++liczba)//po liczbach
    {
        for(int a=0;a<sizeBox;++a)//wiersze Sq
            for(int b=0;b<sizeBox;++b)//kolumny Sq
            {
            juzJest=0;
            QBitArray tempK(sizeBox, false);// = new int[sizeBox];//wektor pomocniczy do zapisu wynikow z kolumn(eliminacja w kolumnie)
            QBitArray tempW(sizeBox, false);// = new int[sizeBox];//wektor pomocniczy do zapisu wynikow z wierszy(eliminacja w wierszu)

            int c=0;
            while(c<sizeBox && !juzJest)//wiersze w Sq
            {
                int d=0;
                while(d<sizeBox && !juzJest)//kolumny w Sq
                {
                    //eliminacja w kolumnach:
                    int cellIndex1 = (a*sizeBox+c)*size + b*sizeBox+d;
                    if(cells[cellIndex1].isEmpty())
                    {
                        if(cells[cellIndex1].getPossibl(liczba) == true)
                        {
                            tempK.setBit(d, true);
                            nrK=b*sizeBox+d;//indeks kolumny
                        }
                    }
                    else if(cells[cellIndex1].getValue()==liczba+1)//jesli liczba juz jest wstawiona to konczy petle bo nie bedzie nidzie possibl=1
                        juzJest=1;//liczba juz jest wstawiona w danym boxie

                    //eliminacja w wierszach:
                    int cellIndex2 = (b*sizeBox+d)*size + a*sizeBox+c;
                    if(cells[cellIndex2].isEmpty())
                    {
                        if(cells[cellIndex2].getPossibl(liczba) == 1)
                        {
                            tempW.setBit(d, true);
                            nrW=b*sizeBox+d;//indeks wiersza
                        }
                    }
                    else if(cells[cellIndex2].getValue()==liczba+1)//jesli liczba juz jest wstawiona to konczy petle bo nie bedzie nidzie possibl=1
                        juzJest=1;//liczba juz jest wstawiona w danym boxie

                    ++d;
                }
                ++c;
            }

            if(!juzJest)
            {
                int ileK = tempK.count(true);//sprawdzenie w ilu kolumnach moze wystepowac liczba
                int ileW = tempW.count(true);//sprawdzenie w ilu wierszach moze wystepowac liczba

                //kolumny:
                if(ileK==1)
                    for(int i=0;i<size;++i)//zmiana prior w kolumnie
                        if( cells[i*size + nrK].isEmpty() &&  (i<a*sizeBox || i>=a*sizeBox+sizeBox) )//jezeli komorka pusta i nie nalezy do aktualnego boxa
                            if(cells[i*size + nrK].getPossibl(liczba)==1)//jezeli jest co zmieniac
                            {
                                onExcludePossibl(i*size + nrK, liczba, LockedCandidates1);//cells[i*size + nrK].setPossibl(liczba, false);
                                ++zmienilo;
                            }
                //wiersze:
                if(ileW==1)
                    for(int i=0;i<size;++i)//zmiana prior w wierszu
                        if( cells[nrW*size + i].isEmpty() && (i<a*sizeBox || i>=a*sizeBox+sizeBox) )//jezeli komorka pusta i nie nalezy do aktualnego boxa
                            if(cells[nrW*size + i].getPossibl(liczba)==1)//jezeli jest co zmieniac
                            {
                                //TODO col/row enum
                                onExcludePossibl(nrW*size + i, liczba, LockedCandidates1);//cells[nrW*size + i].setPossibl(liczba, false);
                                ++zmienilo;
                            }
            }
        }
    }

    qDebug() << "SudokuData::findLockedCandidates1AndExclude" << zmienilo;
    return zmienilo;
}

/*int SudokuData::findLockedCandidates2InVect(QHash<int, SudokuCell*> map, int insertType)
{
    for(int number = 0; number<size; ++number)
    {
        QHashIterator<int, SudokuCell*> i(map);
        int valueInserted = false;
        int cellIndex = 0;
        int b=0;
        QBitArray fillVect(sizeBox, false);
        while(i.hasNext() && !valueInserted)
        {
            i.next();
            if(i.value()->isEmpty())
            {
                    if(i.value()->getPossibl(number )== 1)
                    {
                            fillVect.setBit((b-b%sizeBox)/sizeBox, true);
                            nrK=(b-b%sizeBox)/sizeBox;//indeks kolumnowy boxa
                    }
            }
            else if(cells[a*size + b].getValue()==liczba+1)//jesli liczba juz jest wstawiona to konczy petle bo nie bedzie nidzie possibl=1
                    juzJest=1;//liczba juz jest wstawiona w danym boxie

            ++b;
        }

    }
}*/

int SudokuData::findLockedCandidates2AndExclude()
{

    int zmienilo=0;
    int juzJest;
    int ileK,ileW,nrK,nrW;

    for(int liczba=0;liczba<size;++liczba)//po liczbach
    {
        for(int a=0;a<size;++a)//wiersze
        {
            ileK=0;
            ileW=0;
            juzJest=0;
            QBitArray tempK(sizeBox, false);//wektor pomocniczy do zapisu wynikow z kolumn
            QBitArray tempW(sizeBox, false);//wektor pomocniczy do zapisu wynikow z wierszy

            int b=0;
            while(b<size && !juzJest)//kolumny
            {
                //eliminacja w wierszach:
                int cellIndex1 = a*size + b;
                if(cells[cellIndex1].isEmpty())
                {
                    if(cells[cellIndex1].getPossibl(liczba)==1)
                    {
                        nrK=(b-b%sizeBox)/sizeBox;//indeks kolumnowy boxa
                        tempK.setBit(nrK, true);
                    }
                }
                else if(cells[cellIndex1].getValue()==liczba+1)//jesli liczba juz jest wstawiona to konczy petle bo nie bedzie nidzie possibl=1
                    juzJest=1;//liczba juz jest wstawiona w danym boxie

                //eliminacja w kolumnach:
                int cellIndex2 = b*size + a;
                if(cells[cellIndex2].isEmpty())
                {
                    if(cells[cellIndex2].getPossibl(liczba)==1)
                    {
                        nrW=(b-b%sizeBox)/sizeBox;//indeks wierszowy boxa
                        tempW.setBit(nrW, true);
                    }
                }
                else if(cells[cellIndex2].getValue()==liczba+1)//jesli liczba juz jest wstawiona to konczy petle bo nie bedzie nidzie possibl=1
                    juzJest=1;//liczba juz jest wstawiona w danym boxie

                ++b;
            }

            if(!juzJest)
            {
                ileK = tempK.count(true);//sprawdzenie w ilu boxach w wierszu moze wystepowac liczba
                ileW = tempW.count(true);//sprawdzenie w ilu boxach w kolumnie moze wystepowac liczba

                //wiersze:
                if(ileK==1)
                    for(int i=0;i<sizeBox;++i)//wiersz w box
                        for(int j=0;j<sizeBox;++j)//kolumna w box
                            if( cells[((a-a%sizeBox)+i)*size + nrK*sizeBox+j].isEmpty() && i!=a%sizeBox )//jezeli komorka pusta i nie jest w wierszu analizowanym
                                if(cells[((a-a%sizeBox)+i)*size + nrK*sizeBox+j].getPossibl(liczba)==1)//jezeli jest co zmieniac
                                {
                                    onExcludePossibl(((a-a%sizeBox)+i)*size + nrK*sizeBox+j, liczba, LockedCandidates2);//diag[(a-a%sizeBox)+i][nrK*sizeBox+j].possibl[liczba]=0;
                                    ++zmienilo;
                                }

                //kolumny:
                if(ileW==1)
                    for(int i=0;i<sizeBox;++i)//kolumna w box
                        for(int j=0;j<sizeBox;++j)//wiersz w box
                            if( cells[(nrW*sizeBox+j)*size + (a-a%sizeBox)+i].isEmpty() && i!=a%sizeBox )//jezeli komorka pusta i nie jest w kolumnie analizowanej
                                if(cells[(nrW*sizeBox+j)*size + (a-a%sizeBox)+i].getPossibl(liczba)==1)//jezeli jest co zmieniac
                                {
                                    onExcludePossibl((nrW*sizeBox+j)*size + (a-a%sizeBox)+i, liczba, LockedCandidates2);//diag[nrW*sizeBox+j][(a-a%sizeBox)+i].possibl[liczba]=0;
                                    ++zmienilo;
                                }
            }
        }
    }

    qDebug() << "SudokuData::findLockedCandidates2AndExclude" << zmienilo;
    return zmienilo;
}

void SudokuData::sortByCluesAmount(QList<SudokuCell*> cellVector)//not using this?
{
    /*qDebug() << "before sort:";
    for(int i=0;i<cellVector.size();++i)
        qDebug() << *cellVector.at(i);*/

    qSort(cellVector.begin(), cellVector.end(), SudokuCell::hasLessClues);//SudokuCell::hasMoreClues);

    /*qDebug() << "after sort:";
    for(int i=0;i<cellVector.size();++i)
        qDebug() << *cellVector.at(i);*/
}

int SudokuData::findGroupsAndExclude()
{
    int count = 0;

    for(int i=0;i<size;++i)//cells in row/col/box from 1 to size
    {
        //QHash<int, SudokuCell*> rowMap;
        //QHash<int, SudokuCell*> colMap;
        //QHash<int, SudokuCell*> boxMap;

        QList<SudokuCell*> vectRow;
        QList<SudokuCell*> vectCol;
        QList<SudokuCell*> vectBox;

        int boxBaseIndex = (i%sizeBox)*sizeBox + (i/sizeBox)*size*sizeBox;

        for(int j=0;j<size;++j)
        {
            //rowMap[i*size + j] = &cells[i*size + j];
            //colMap[j*size + i] = &cells[j*size + i];
            vectRow.append(&cells[i*size + j]);
            vectCol.append(&cells[j*size + i]);
            int cellBoxIndex = boxBaseIndex + j%sizeBox + (j/sizeBox)*size;
            //boxMap[cellBoxIndex] = &cells[cellBoxIndex];
            vectBox.append(&cells[cellBoxIndex]);
        }
        sortByCluesAmount(vectRow);
        sortByCluesAmount(vectCol);
        sortByCluesAmount(vectBox);


        count += findGroupsInVectorAndExclude(vectRow);//findHiddenSinglesInVect(rowMap, HiddenSinglesRow);
        count += findGroupsInVectorAndExclude(vectCol);//findHiddenSinglesInVect(colMap, HiddenSinglesCol);
        count += findGroupsInVectorAndExclude(vectBox);//findHiddenSinglesInVect(boxMap, HiddenSinglesBox);

    }

    qDebug() << "SudokuData::findGroupsAndExclude: " << count;
    return count;
}

//dodac sprawdzanie ilosci pozostalych komorek i czy mozliwe znalezienie grupy danej wielkosci. oba fory na while!
int SudokuData::findGroupsInVectorAndExclude(QList<SudokuCell*> cellVector)//wyszukuje wszystkie grupy w zadanym wektorze i redukuje possibl
{
        int zmienilo=0;

        //TODO
        for(int i=size-1;i>=1;--i)//posortowane od najmniejszego, jedzie od najwiekszej ilosci podpowiedzi, najmniejszej nie sprawdza
        {
                if(cellVector[i]->isEmpty())//TODO jak znajdzie komorke wypelniana to moze przerwac bo dalej juz same wypelnione
                {
                        int ilkom=1;//ilkom=1 bo 1 komorka grupy to ta sprawdzana
                        int mozliwosci = cellVector[i]->countPossible();
                        QBitArray znalezioneCell(size, false);// = new bool [size];//tablica mowiaca o tym, ktore komorki wchodza w sklad znalezionej grupy
                        znalezioneCell.setBit(i, true);//dana komorka jest 1 znaleziona

                        //TODO co dla komorek ktore kiedys mialy prior0 a teraz inny? jak zostana posortowane?
                        if(mozliwosci>=2 && mozliwosci<=size-2 && mozliwosci<=i+1)//size-2 bo dla size-1 zlapie metoda findNakedSingles(); tylko jesli zostalo wystarczajaco duzo komorek
                        {
                                int k=i-1;
                                do{//od danej komorki do poczatku
                                        if(cellVector[k]->isEmpty())
                                        {
                                                int ilZgodnychPrior=0, blad=0, l=0;
                                                do{//po possibl
                                                        if( cellVector[i]->getPossibl(l)==0 && cellVector[k]->getPossibl(l)==1)//jezeli w komorce porownywanej jest inna liczba niz w danej
                                                                blad=1;
                                                        else if(cellVector[i]->getPossibl(l)==1)//zakladam ze nie ma w diagrami komorek w ktore nic nie mozna wpisac
                                                                ilZgodnychPrior++;
                                                        ++l;
                                                }while(l<size && !blad);
                                                if(ilZgodnychPrior==mozliwosci && !blad)//znaleziono zgodna komorke
                                                {
                                                        ilkom++;
                                                        znalezioneCell.setBit(k, true);
                                                }
                                        }
                                        --k;
                                }while(k>=0 && mozliwosci-ilkom<=k+1);//jesli pozostalo wystarczajaco duzo komorek zeby znalezc grupe

                                if(ilkom==mozliwosci)//znalazlo grupe
                                {
                                        for(int m=0;m<size;++m)//po possibl
                                                if(cellVector[i]->getPossibl(m))//jesli 1 komorka grupy nie zawiera wszystkich cyfr to moze zle dzialac!!!!!!!!!!
                                                        for(int n=0;n<size;++n)//po komorkach
                                                                if(!znalezioneCell.testBit(n) && cellVector[n]->isEmpty())//jesli komorka nie nalezy do znalezionej grupy i jest pusta
                                                                        if(cellVector[n]->getPossibl(m)==1)//jesli jest co zmieniac
                                                                        {
                                                                                //TODO onFound!!!
                                                                                cellVector[n]->setPossibl(m, false);//zmiana possibl w diag
                                                                                ++groupsCount;
                                                                                ++zmienilo;
                                                                        }
                                }
                                //else if(ilkom>mozliwosci)
                                //	;//moznaby dodac else jesli wiecej ilkom>mozliwosci to nie moze miec rozwiazania
                        }
                }
        }

        return zmienilo;
}

int SudokuData::xWing()//TODO dodac to samo po kolumnach?
{
    int zmienilo=0;
    int indK[2],ileK,indW[2],ileW;
    int j;

    for(int liczba=0;liczba<size;++liczba)//po liczbach
    {
        for(int i=0;i<size;++i)//po wierszach, kolumnach w 2 czesci
        {
            ileK=0;
            j=0;
            do{//po kolumnach
                if(cells[i*size + j].isEmpty())
                {
                    if(cells[i*size + j].getPossibl(liczba))
                    {
                        if(ileK<2)
                            indK[ileK]=j;
                        ++ileK;
                    }
                }
                ++j;
            }while(j<size && ileK<=2);

            if(ileK==2)//jesli znaleziono mozliwosc wpisania tylko w 2 miejscach w wierszu
            {
                int k=i+1;
                while(k<size)//po wieszach ktore sa nizej, szukam 2 wiersza z takimi samymi podpowiedziami w 2 miejscach
                {
                    if(cells[k*size + indK[0]].isEmpty() && cells[k*size + indK[1]].isEmpty())
                    {
                        if(cells[k*size + indK[0]].getPossibl(liczba) && cells[k*size + indK[1]].getPossibl(liczba))//jezeli 1 i 2 sie zgadza
                        {
                            int l=0;
                            bool blad=0;
                            do{//sprawdzam czy w wierszu nie ma mozliwosci w jeszcze innym miejscu niz 2 znalezione
                                if(l!=indK[0] && l!=indK[1] && cells[k*size + l].isEmpty())
                                    if(cells[k*size + l].getPossibl(liczba))
                                        blad=1;//jest trzecia komorka gdzie mozna wpisac
                                ++l;
                            }while(l<size && !blad);
                            if(!blad)//znaleziono czworke xWing
                            {
                                for(int m=0;m<size;++m)
                                {
                                    if(m!=i && m!=k)
                                    {
                                        if(cells[m*size + indK[0]].isEmpty())
                                        {
                                            if(cells[m*size + indK[0]].getPossibl(liczba)==1)//jezeli jest co zmieniac
                                            {
                                                //TODO
                                                cells[m*size + indK[0]].setPossibl(liczba, false);
                                                ++zmienilo;
                                                ++xWingCount;
                                            }
                                        }
                                        if(cells[m*size + indK[1]].isEmpty())
                                        {
                                            if(cells[m*size + indK[1]].getPossibl(liczba)==1)//jezeli jest co zmieniac
                                            {
                                                //TODO
                                                cells[m*size + indK[1]].setPossibl(liczba, false);
                                                ++zmienilo;
                                                ++xWingCount;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    ++k;
                }
            }

            ileW=0;
            j=0;
            do{//po wierszach
                if(cells[j*size + i].isEmpty())
                {
                    if(cells[j*size + i].getPossibl(liczba))
                    {
                        if(ileW<2)
                            indW[ileW]=j;
                        ++ileW;
                    }
                }
                ++j;
            }while(j<size && ileW<=2);

            if(ileW==2)//jesli znaleziono mozliwosc wpisania tylko w 2 miejscach w kolumnie
            {
                int k=i+1;
                while(k<size)//po kolumnach ktore sa z prawej, szukam 2 kolumny z takimi samymi podpowiedziami w 2 miejscach
                {
                    if(cells[indW[0] * size + k].isEmpty() && cells[indW[1] * size + k].isEmpty())
                    {
                        if(cells[indW[0] * size + k].getPossibl(liczba) && cells[indW[1] * size + k].getPossibl(liczba))//jezeli 1 i 2 sie zgadza
                        {
                            int l=0;
                            bool blad=0;
                            do{//sprawdzam czy w kolumnie nie ma mozliwosci w jeszcze innym miejscu niz 2 znalezione
                                if(l!=indW[0] && l!=indW[1] && cells[l*size + k].isEmpty())
                                    if(cells[l*size + k].getPossibl(liczba))
                                        blad=1;//jest trzecia komorka gdzie mozna wpisac
                                ++l;
                            }while(l<size && !blad);
                            if(!blad)//znaleziono czworke xWing
                            {
                                for(int m=0;m<size;++m)
                                {
                                    if(m!=i && m!=k)
                                    {
                                        if(cells[indW[0] * size + m].isEmpty())
                                        {
                                            if(cells[indW[0] * size + m].getPossibl(liczba)==1)//jezeli jest co zmieniac
                                            {
                                                //TODO
                                                cells[indW[0] * size + m].setPossibl(liczba,false);
                                                ++zmienilo;
                                                ++xWingCount;
                                            }
                                        }
                                        if(cells[indW[1] * size + m].isEmpty())
                                        {
                                            if(cells[indW[1] * size + m].getPossibl(liczba)==1)//jezeli jest co zmieniac
                                            {
                                                //TODO
                                                cells[indW[1] * size + m].setPossibl(liczba, false);
                                                ++zmienilo;
                                                ++xWingCount;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    ++k;
                }
            }
        }
    }
    return zmienilo;
}

void SudokuData::onFoundValue(int row, int col, int value, int method)
{
    qDebug() << "row" << row+1 << "col" << col+1 << "value" << value << "method" << method;
    if(isBadNumber(row,col,value))//TODO
    {
        qDebug() << "row" << row+1 << "col" << col+1 << "value" << value << "method" << method;
        sleep(2);
        qFatal("Bad number found");
    }
    cells[row*size + col].setValue(value,method);
    excludePossibl(row, col, value);
    qDebug() << "SudokuData::onFoundValue";
    if(!isSolvable())//TODO
        qCritical("no solution!!!!!!!!!!!!!!");
    emit valueFound(row, col, value, method);//(*this);
    //sleep(1);//TODO !!!
}

void SudokuData::onFoundValue(int index, int value, int method)
{
    int row = index / size;
    int col = index % size;

    //qDebug() << "index: " << index << "counted index: " << row * size + col;

    onFoundValue(row, col, value, method);
}

void SudokuData::onExcludePossibl(int index, int number, int method)
{
    cells[index].setPossibl(number, false);
    if(method == LockedCandidates1)
        ++lockedCandidates1Count;
    else if(method == LockedCandidates2)
        ++lockedCandidates2Count;
    else if(method == Groups)
        ++groupsCount;

    emit possiblExcluded(index);
    qDebug() << "SudokuData::onExcludePossibl";
}

QList<SudokuCell*> SudokuData::getBox(int index)
{
    if(index < 0 || index >= size)
        qFatal("SudokuData::getBox wrong index");

    QList<SudokuCell*> cellPointerList;

    int boxBaseIndex = (index%sizeBox)*sizeBox + (index/sizeBox)*size*sizeBox;

    for(int cellInd=0;cellInd<size;++cellInd)
    {
        int cellBoxIndex = boxBaseIndex + cellInd%sizeBox + (cellInd/sizeBox)*size;
        cellPointerList.append(&cells[cellBoxIndex]);
    }

    return cellPointerList;
}

int SudokuData::getSize()
{
    return size;
}

int SudokuData::getSizeBox()
{
    return sizeBox;
}

int SudokuData::getCellCount()
{
    return cells.size();
}

int SudokuData::getCellCount(int cellType)
{
    int count = 0;

    for(int i=0;i<cells.size();++i)
        if(cells[i].getPrior() & cellType)
            ++count;

    return count;
}

int SudokuData::getLockedCandidates1Count()
{
    return lockedCandidates1Count;
}

int SudokuData::getLockedCandidates2Count()
{
    return lockedCandidates2Count;
}

int SudokuData::getGroupsCount()
{
    return groupsCount;
}

int SudokuData::getXwingCount()
{
    return xWingCount;
}

void SudokuData::clear()
{
    lockedCandidates1Count = 0;
    lockedCandidates2Count = 0;
    groupsCount = 0;
    xWingCount = 0;

    for(int i=0;i<cells.size();++i)
        if(cells[i].getPrior() != Certain)
            cells[i].clear();
    excludeAllPossibl();
}

const SudokuCell& SudokuData::cellAt(int row, int col) const
{
    return cells.at(row * size + col);
}
const SudokuCell& SudokuData::cellAt(int index) const
{
    return cells.at(index);
}

SudokuCell& SudokuData::operator[](int index)
{
    return cells[index];
}

double SudokuData::countPossibleSolutions()
{
    double count = 1;
    QString mulStr("");
    for(int i=0;i<cells.size();++i)
        if(cells[i].getValue() == 0)
        {
            //qDebug() << "cell possibl: " << cells[i].possiblArr.count(true);
            mulStr += QString::number(cells[i].countPossible()) + "*";
            count *= cells[i].countPossible();
            qDebug() << "count: " << count;
        }
    qDebug() << mulStr << " = " << count;
    return count;

}

int SudokuData::countIndividuals()
{
    int indivCount = 0;
    QBitArray countRow(size);
    QBitArray countCol(size);
    QBitArray countBox(size);

    for(int i=0;i<size;++i)
    {
        countRow.fill(false);
        countCol.fill(false);
        countBox.fill(false);

        int boxBaseIndex = (i%sizeBox)*sizeBox + (i/sizeBox)*size*sizeBox;

        for(int j=0;j<size;++j)
        {
            countRow.setBit(cellAt(i,j).getValue() - 1);
            countCol.setBit(cellAt(j,i).getValue() - 1);
            int cellBoxIndex = boxBaseIndex + j%sizeBox + (j/sizeBox)*size;
            countBox.setBit(cellAt(cellBoxIndex).getValue() - 1);
        }

        //qDebug() << "row: " << countRow.count(true) << "col: " << countCol.count(true) << "box: " << countBox.count(true);
        indivCount += countRow.count(true);
        indivCount += countCol.count(true);
        indivCount += countBox.count(true);
    }
    //qDebug() << "indivCount " << indivCount;
    return indivCount;
}

void SudokuData::solve(int solveMask)
{
    bool foundRun;

    do{

        foundRun = false;
        if(solveMask & NakedSingles)
        {
            while(findNakedSingles())
                foundRun = true;
        }

        if(solveMask & HiddenSingles)
        {
            while(findHiddenSingles())
                foundRun = true;
        }

        if(solveMask & LockedCandidates1)
        {
            while(findLockedCandidates1AndExclude())
                foundRun = true;
        }

        if(solveMask & LockedCandidates2)
        {
            while(findLockedCandidates2AndExclude())
                foundRun = true;
        }

        if(solveMask & Groups)
        {
            while(findGroupsAndExclude())
                foundRun = true;
        }

        if(solveMask & Xwing)
        {
            while(xWing())
                foundRun = true;
        }
        //qDebug() << "foundRun:" << foundRun;

    }while(foundRun == true);
}

bool SudokuData::isBadNumber(int wier, int kol, int wartosc, bool sure)//sprawdza oba warunki sudoku, zwraca 0 jezeli nie ma bledu
{
    //if(cells[wier*size + kol].getPossibl(wartosc-1) == 0)
    //    return true;

        for(int adr=0;adr<size;++adr)
                if(adr!=wier&&cells[adr*size + kol].getValue() == wartosc&&(cells[adr*size + kol].getPrior()!=Empty || !sure) || adr!=kol&&cells[wier*size + adr].getValue()==wartosc&&(cells[wier*size + adr].getPrior()!=0 || !sure))//zeby nie sprawdzac porownywanej komorki, sprawdzanie wiersza i kolumny oprocz danej komorki
                        return 1;

        int l1=wier%sizeBox;
        int l2=kol%sizeBox;

        for(int i=0;i<sizeBox;++i)
                for(int j=0;j<sizeBox;++j)
                        if( (l1!=i || l2!=j) && cells[(wier-l1+i)*size + kol-l2+j].getValue()==wartosc && (cells[(wier-l1+i)*size + kol-l2+j].getPrior()!=Empty || !sure))//zeby nie sprawdzac porownywanej komorki i jesli komorka biezaca==komorce danej
                                return 1;

        return 0;
}

//ta funkcje moznaby jeszcze dopracowac
bool SudokuData::isSolvable()//sprawdza czy wczytany diagram jest poprawny i czy moze miec rozwiazanie
{
        for(int i=0;i<size;++i)
                for(int j=0;j<size;++j)
                        if(cells[i*size + j].getValue()!=0)//jesli komorka wypelniona to sprawdza czy nie ma sprzecznosci
                        {
                                if(isBadNumber(i,j,cells[i*size + j].getValue(),0))//jesli blad
                                        return 0;
                        }
                        else//jesli komorka pusta to sprawdza czy jest mozliwe wpisanie czegokolwiek
                        {
                            if(cells[i*size + j].countPossible() == 0)
                                return false;
                        }
        //TODO previous checking possibly not needed. checkDiagram is enough
        return checkDiagram();//1;
}

bool SudokuData::isFilled()//sprawdza czy diagram jest wypelniony liczbami z prior>1, uzywane do spr. czy rozwiazane przed uruchomieniem BF
{
    for(int i=0;i<size;++i)
        for(int j=0;j<size;++j)
            if(cells[i*size + j].isEmpty())
                return false;
    return true;
}

bool SudokuData::isSolved()
{
    if(!isFilled())
        return false;
    if(countIndividuals() < size * size * 3)
        return false;
    return true;
}

bool SudokuData::isValid()
{
    if(isSolved())
        return true;

    if( !isFilled() && isSolvable())
        return true;

    return false;
}

bool SudokuData::checkVector(QList<SudokuCell*> cellVector)
{
    QVector<int> insertedValues;
    insertedValues.fill(0,cellVector.size());
    QVector<int> possibleValues;
    possibleValues.fill(0,cellVector.size());

    for(int i=0; i<cellVector.size(); ++i)
    {
        if(cellVector.at(i)->isEmpty())
        {
            for(int clue=0 ;clue<cellVector.at(i)->getPossiblArrSize(); ++clue)
            {
                possibleValues[clue] += cellVector.at(i)->getPossibl(clue);
            }
        }
        else
        {
            insertedValues[cellVector.at(i)->getValue()-1] += 1;
        }
    }

    /*
    qDebug() << "insertedValues: " << insertedValues;
    qDebug() << "possibleValues: " << possibleValues;*/

    for(int ind=0;ind<cellVector.size(); ++ind)
    {
        if(insertedValues.at(ind)>1)//2 numbers inserted
        {
            qDebug() << "SudokuData::checkVector 2 numbers inserted " << ind;
            return false;
        }

        if(insertedValues.at(ind) == 0)//vector doesn't contain number
        {
            if(possibleValues.at(ind)<1)//if there is no cell to put that number
            {
                qDebug() << "SudokuData::checkVector no cell to put number " << ind;
                return false;
            }
        }
    }

    return true;
}

bool SudokuData::checkDiagram()
{
    for(int i=0;i<size;++i)//cells in row/col/box from 1 to size
    {
        QList<SudokuCell*> rowVect;
        QList<SudokuCell*> colVect;
        QList<SudokuCell*> boxVect;

        int boxBaseIndex = (i%sizeBox)*sizeBox + (i/sizeBox)*size*sizeBox;

        for(int j=0;j<size;++j)
        {
            rowVect.append(&cells[i*size + j]);
            colVect.append(&cells[j*size + i]);
            int cellBoxIndex = boxBaseIndex + j%sizeBox + (j/sizeBox)*size;
            boxVect.append(&cells[cellBoxIndex]);
        }
        if(!checkVector(rowVect))
        {
            qDebug() << "SudokuData::checkDiagram row " << i;
            return false;
        }

        if(!checkVector(colVect))
        {
            qDebug() << "SudokuData::checkDiagram col " << i;
            return false;
        }

        if(!checkVector(boxVect))
        {
            qDebug() << "SudokuData::checkDiagram box " << i;
            return false;
        }
    }

    //qDebug() << "SudokuData::checkDiagram";
    return true;
}

bool SudokuData::saveToFile(QString filePath, QString fileName)
{
    qFatal("not implemented");
    QFile* file = new QFile (filePath + fileName);

    if ( !file->open(QIODevice::WriteOnly) )
        return false;

    //file->write ( ... );//TODO save as XML with prior
    file->close ();

    return true;
}

QByteArray SudokuData::readFromFile(QString filePath, QString fileName)
{
    QFile* file = new QFile (filePath + fileName);

    if ( !file->open(QIODevice::ReadOnly) )
        return false;

    QByteArray array;
    array = file->readAll();
    file->close ();

    return array;
}

QDebug operator<<(QDebug dbg, const SudokuData& sud)
{
    for(int i=0;i<sud.size;++i)
    {
        for(int j=0;j<sud.size;++j)
        {
            dbg.nospace() << sud.cells[i*sud.size + j] << '.';
        }
        dbg.nospace() << endl;
    }

    return dbg.maybeSpace();
}
